# -- coding: utf-8 --
"""
# MIAHX© 时间同步消息的 FastDDS 发布/订阅器

**模块：MIAHX© / 框架基础服务层（FFST） / 交互框架信息表示模型（IFIRM）**

Version: 0.4.10,
Last updated: October 20, 2024

## Contributor(s)
* YAO Naiming <[naiming@iscas.ac.cn](mailto:naiming@iscas.ac.cn)>

## Change log
### v0.4.10
- .20: [优化] Reader 构造方法传入 topic_name
- .6: [新增] GNSSWriter 新增 periods 字段
- .2: [重构] 重构
### v0.4.7
- .24: [新增] GNSSWriter 新增 MsgHeader 字段
### v0.4.5
- .16: [优化] 合并 reader 和 writer，支持 participant 共享
### v0.4.5
- .5: [新增] 初始实现

---
Copyright © 2024-2025 MIAHX Contributors, ISCAS, and ASIC. All Rights Reserved.
Licensed under Apache-2.0.
"""

import datetime
import multiprocessing
import os
import socket
import typing

import fastdds
import miahx_fastdds as dds
from loguru import logger

from .participant import Participant
from .reader import Reader, ReaderListener
from .topics import Topics
from .writer import Writer

_log = logger.bind(module_name=os.path.basename(__file__))


class GNSSReaderListener(ReaderListener):
    def __init__(self, topic_name: str, topic_data_type: fastdds.TopicDataType,
                 on_data_listeners=None, messenger=None):
        super().__init__(topic_name, topic_data_type, on_data_listeners)
        self._sync_cond = None
        self._sync_data = None
        if isinstance(messenger, tuple) and 2 == len(messenger):
            self._sync_cond, self._sync_data = messenger

    def on_data_available(self, reader):
        try:
            info = fastdds.SampleInfo()
            create_data = getattr(dds, self.topic_data_type.getName())
            data = create_data()
            assert isinstance(data, dds.TimeSyncMsg)
            if reader.take_next_sample(data, info) == fastdds.ReturnCode_t.RETCODE_OK:
                if info.valid_data:
                    self.notify_all_listeners(data)
                    self._notify_all_clients(data, status='ok')
        except Exception as ex:
            _log.error(f'{ex}')

    def shutdown(self):
        self._notify_all_clients(None, status='shutdown')

    def _notify_all_clients(self, data: typing.Optional[dds.TimeSyncMsg], status='ok'):
        if self._sync_cond is not None and self._sync_data is not None:
            assert isinstance(self._sync_cond, multiprocessing.managers.ConditionProxy)
            assert isinstance(self._sync_data, multiprocessing.managers.DictProxy)
            if data is not None:
                dt = datetime.datetime(data.year(), data.month(), data.day(),
                                       data.hour(), data.min(), data.sec(),
                                       int(data.ns() / 1000.0))
                uts = dt.timestamp()
                iid = data.iid()
            else:
                uts, iid = None, None
            with self._sync_cond:
                self._sync_data.update(dict(status=status, iid=iid, uts=uts))
                self._sync_cond.notify_all()


class GNSSReader(Reader):
    def __init__(self, participant: Participant, on_data_listeners=None, messenger=None):
        topic_name = Topics.TIME_SYNC
        topic_data_type = dds.TimeSyncMsgPubSubType()
        super().__init__(participant, topic_name, topic_data_type,
                         listener=GNSSReaderListener(topic_name, topic_data_type, on_data_listeners, messenger))


class GNSSWriter(Writer):
    """
    时间同步消息写入器
    """

    def __init__(self, participant: Participant):
        super().__init__(participant, Topics.TIME_SYNC, dds.TimeSyncMsgPubSubType(), stag=None)

    def write(self,
              year: int, month: int, day: int,
              hour: int, minute: int, second: int, ns: int = 0,
              isn: int = 0, periods: float = 1.0):
        """
        创建 GPS 仿真传感器的数据获取线程

        :param year: 年
        :param month: 月
        :param day: 日
        :param hour: 小时
        :param minute: 分钟
        :param second: 秒
        :param ns: 纳秒（10e-9秒）
        :param isn: 全局单调递增序号
        :param periods: 时间同步周期，单位秒，默认 1.0
        """

        data = dds.TimeSyncMsg()
        # header
        header = dds.MiahxMsgHeader()
        header.domain(self.domain)
        header.source(socket.gethostname())
        data.header(header)
        # body
        data.year(year)
        data.month(month)
        data.day(day)
        data.hour(hour)
        data.min(minute)
        data.sec(second)
        data.ns(ns)
        data.iid(isn)
        data.periods(periods)
        # send
        self._writer.write(data)
